package com.riccardocardin.structured.concurrency.service;

import com.riccardocardin.structured.concurrency.policy.ShutdownOnResult;
import com.riccardocardin.structured.concurrency.util.Concurrents;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ExecutionException;

import static com.riccardocardin.structured.concurrency.model.Model.*;

public class RepositoryServiceImpl implements RepositoryService {

    private static final Logger LOGGER = LoggerFactory.getLogger("GitHubApp");

    @Override
    public List<Repository> findRepositories(UserId userId) throws InterruptedException, ExecutionException {
        LOGGER.info("Finding repositories for user with id '{}'", userId);
        delay(Duration.ofSeconds(1L));
        //throw new RuntimeException("Socket timeout");
        LOGGER.info("Repositories found for user '{}'", userId);
        return List.of(
                new Repository(
                        "raise4s",
                        Visibility.PUBLIC,
                        URI.create("https://github.com/rcardin/raise4s")),
                new Repository(
                        "sus4s",
                        Visibility.PUBLIC,
                        URI.create("https://github.com/rcardin/sus4s")));
    }

    public List<Repository> findRepositories(UserId userId, Duration timeout)
            throws InterruptedException, ExecutionException {
        try (var scope = new ShutdownOnResult<List<Repository>>()) {
            scope.fork(() -> findRepositories(userId));
            scope.fork(() -> {
                delay(timeout);
                throw new RuntimeException("Timeout of %s reached".formatted(timeout));
            });
            return scope.join().resultOrThrow();
        }
    }

    // or use generic Concurrents.race method
    public List<Repository> findRepositoriesByRace(UserId userId, Duration timeout)
            throws InterruptedException, ExecutionException {
        return Concurrents.race(() -> findRepositories(userId),
                () -> {
                    delay(timeout);
                    throw new RuntimeException("Timeout of %s reached".formatted(timeout));
                });
    }

    void delay(Duration duration) throws InterruptedException {
        Thread.sleep(duration);
    }
}
